package com.fluidops.keyword;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.openrdf.model.Literal;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.ntriples.NTriplesWriter;
import org.openrdf.sail.Sail;

import com.fluidops.keyword.label.provider.Vocabulary.DCAT;
import com.fluidops.keyword.label.provider.Vocabulary.DCTERMS;
import com.fluidops.keyword.util.SearchResult;

public class KeywordSearch {

	public static final String QUIT_COMMAND = "quit";
	public static final String QUIT_ABREV_COMMAND = "q";

	public static final String PRINT_ABREV_COMMAND = "s";
	public static final String PRINT_COMMAND = "save";

	public static final String KEYWORD_SEARCH_META = "http://datacatalog.eval/meta/kwsearch";
	public static final String NUMBER_SEARCH_RESULTS = "http://datacatalog.eval/meta/numResults";
	public static final String ENTERED_KEYWORD_QUERY = "http://datacatalog.eval/meta/kwquery";
	
	public static final String CATALOG_SUBJECT = "http://datacatalog.eval/Catalog/";
	public static final String DISTRIBUTION_DUMMY = "http://datacatalog.eval/Distribution/";

	public static final String SCORE_PROPERTY = "http://datacatalog.eval/score/";
	public static final String RANK_PROPERTY = "http://datacatalog.eval/rank/";

	public static final String DISTRIB_FORMAT = "application/rdf+xml";

	public static final String PROMPT = "Please enter the keyword. (Press \"" + QUIT_ABREV_COMMAND +"\" or type \"" + QUIT_COMMAND 
			+"\" to finish, Press \"" + PRINT_COMMAND + "\" or \"" 
			+ PRINT_ABREV_COMMAND + "\" to save the last results) ";

	public static final String RESULT_BASE_PATH = "C:\\DA\\SearchResults\\";

	private static AtomicInteger numCatalogs = new AtomicInteger(0);

	public static final int NUM_RESULTS = 100;

	private static Logger logger = Logger.getLogger(KeywordSearch.class.getName());

	public static void main(String[] args) throws Exception{
		if(args.length > 0){
			Properties prop = new Properties();
			prop.load(new FileReader(args[0]));

			PropertyConfigurator.configure(prop.getProperty(RepoCreate.LOGGER_PROPERTY));

			String location = prop.getProperty(RepoCreate.REPO_LOCATION_PROPERTY);
			logger.info("Set repository location to " + location);
			Repository repo = initRepo(location);

			userInteraction(repo);

			repo.shutDown();
		}

	}


	public static void userInteraction(Repository repo) throws Exception{
		boolean finished = false;
		RepositoryConnection con = repo.getConnection();
		Scanner sc = new Scanner(System.in);

		String command = "";
		String lastCommand;
		TreeSet<SearchResult> resultURIs = null;
		while(!finished){
			System.out.println(PROMPT);
			lastCommand = command;
			command = sc.nextLine();
			finished = wasQuitCommand(command);

			if(wasPrintCommand(command) && !lastCommand.equals("") && resultURIs != null){
				saveResults(resultURIs, lastCommand);
			}

			if(!finished && !wasPrintCommand(command)){
				resultURIs = getKeywordResults(con, command);
				printResult(resultURIs);

			}
		}
		sc.close();
		con.close();
	}

	private static void saveResults(TreeSet<SearchResult> resultURIs, String command) throws Exception{
		File f = new File(RESULT_BASE_PATH + command + ".nt");
		f.createNewFile();
		NTriplesWriter ntw = new NTriplesWriter(new BufferedWriter(new FileWriter(f))) ;
		ntw.startRDF();

		int printedResults = 1;

		ValueFactory vf = ValueFactoryImpl.getInstance();
		URI catalogSubject = vf.createURI(CATALOG_SUBJECT + numCatalogs.getAndIncrement());
		Statement s;
		
		s = vf.createStatement(vf.createURI(KEYWORD_SEARCH_META), vf.createURI(NUMBER_SEARCH_RESULTS), vf.createLiteral(resultURIs.size()));
		ntw.handleStatement(s);
		s = vf.createStatement(vf.createURI(KEYWORD_SEARCH_META), vf.createURI(ENTERED_KEYWORD_QUERY), vf.createLiteral(command));
		ntw.handleStatement(s);
		
		s = vf.createStatement(catalogSubject, RDF.TYPE, DCAT.CATALOG);
		ntw.handleStatement(s);
		s = vf.createStatement(catalogSubject, DCAT.KEYWORD, vf.createLiteral(command));
		ntw.handleStatement(s);

		for(SearchResult sr : resultURIs.descendingSet()){
			s = vf.createStatement(catalogSubject, DCAT.DATASET_PROPERTY, sr.getUri());
			ntw.handleStatement(s);
			s = vf.createStatement(sr.getUri(), RDF.TYPE, DCAT.DATASET);
			ntw.handleStatement(s);
			s = vf.createStatement(sr.getUri(), vf.createURI(SCORE_PROPERTY), vf.createLiteral(sr.getScore()));
			ntw.handleStatement(s);
			s = vf.createStatement(sr.getUri(), vf.createURI(RANK_PROPERTY), vf.createLiteral(printedResults));
			ntw.handleStatement(s);


			s = vf.createStatement(sr.getUri(), DCAT.DISTRIBUTION_PROPERTY, vf.createURI(DISTRIBUTION_DUMMY + sr.getUri().getLocalName()));
			ntw.handleStatement(s);

			if(sr.getUri().stringValue().contains("worldbank")){
				URI accessURL = vf.createURI("http://worldbank.270a.info/data/world-development-indicators/" + sr.getUri().getLocalName() + ".rdf");
				s = vf.createStatement(vf.createURI(DISTRIBUTION_DUMMY + sr.getUri().getLocalName()), DCAT.ACCESS_URL, accessURL);
			}else{
				// no adaption needed for eurostat data dumps
				s = vf.createStatement(vf.createURI(DISTRIBUTION_DUMMY + sr.getUri().getLocalName()), DCAT.ACCESS_URL, vf.createLiteral(sr.getUri().stringValue()+ ".rdf"));
			}
			ntw.handleStatement(s);

			s = vf.createStatement(vf.createURI(DISTRIBUTION_DUMMY + sr.getUri().getLocalName()), DCTERMS.FORMAT, vf.createLiteral(DISTRIB_FORMAT));
			ntw.handleStatement(s);

			printedResults++;
			if(printedResults == NUM_RESULTS + 1){
				break;
			}
		}

		ntw.endRDF();
	}

	private static void printResult(TreeSet<SearchResult> resultURIs){
		int numResult = 1;
		for(SearchResult entry : resultURIs.descendingSet()){
			System.out.print(numResult++);
			System.out.println(formatResult(entry));

			if(numResult == NUM_RESULTS + 1){
				break;
			}
		}
	}

	private static boolean wasPrintCommand(String command){
		if(PRINT_COMMAND.equalsIgnoreCase(command) || PRINT_ABREV_COMMAND.equalsIgnoreCase(command)){
			return true;
		}
		return false;
	}

	private static String formatResult(SearchResult entry){
		StringBuilder sb = new StringBuilder(": <");
		sb.append(entry.getUri().stringValue()).append("> \t with a score of: ");
		sb.append(entry.getScore());
		return sb.toString();
	}

	private static TreeSet<SearchResult> getKeywordResults(RepositoryConnection con, String command) throws Exception{
		List<SearchResult> resultURIs = new LinkedList<SearchResult>();

		StringBuilder query = new StringBuilder(" PREFIX luc: <http://www.ontotext.com/owlim/lucene#> ");
		query.append(" SELECT DISTINCT ?subject ?score ");

		query.append(" WHERE { ?subject ?property ?value .");
		query.append(" ?value luc:").append(RepoCreate.INDEX_NAME).append(" \"").append(command).append("\" ; ");
		query.append(" luc:score ?score . ");
		// possibly luc:snippet as introduced in forum post (http://www.mail-archive.com/owlim-discussion@ontotext.com/msg00409.html)
 		query.append(" } ");
		//	query.append(" LIMIT ").append(NUM_RESULTS);


		TupleQuery tquery = con.prepareTupleQuery(QueryLanguage.SPARQL, query.toString());
		TupleQueryResult result = null;
		try{
			result = tquery.evaluate();

			BindingSet bs;
			URI singleURI;
			Literal score;
			boolean add;
			while(result.hasNext()){
				bs = result.next();
				if(bs.getValue("subject") instanceof URI && bs.getValue("score") instanceof Literal){
					singleURI = (URI) bs.getValue("subject");
					score = (Literal) bs.getValue("score");
					add = true;
					for(SearchResult s : resultURIs){
						if(s.getUri().equals(singleURI)){
							s.addScore(score.doubleValue());
							add = false;
							break;
						}
					}
					if(add){
						resultURIs.add(new SearchResult(singleURI, score.doubleValue()));
					}
				}
			}
		} finally{
			if(result != null){
				result.close();
			}
		}
		return new TreeSet<SearchResult>(resultURIs);
	}

	private static boolean wasQuitCommand(String command){
		boolean wasQuit = false;
		if(QUIT_ABREV_COMMAND.equalsIgnoreCase(command) || QUIT_COMMAND.equalsIgnoreCase(command)){
			wasQuit = true;
		}
		return wasQuit;
	}





	public static Repository initRepo(String location) throws Exception{
		Repository repo;

		System.setProperty("ruleset", "empty"); // for performance reasons
		System.setProperty("repository-type", "weighted-file-repository");
		System.setProperty("console-thread", "false");
		System.setProperty("storage-folder", location);
		System.setProperty("build-pcsot", "true");
		repo = new SailRepository((Sail) 
				Class.forName("com.ontotext.trree.OwlimSchemaRepository")
				.getConstructor().newInstance());
		repo.initialize();
		logger.info("Repository initialized at " + location);

		return repo;
	}

}
